/**
 * ConstraintAbstract
 * ----------------------------------------------
 * 
 * This abstract class represents integrity constraints for XDF, XDFC;
 * 
 * - XDF extends  
 * - XDFC extends
 *  
 * @author caio thomas
 * @version 
 */
package model.constraints;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import automata.Alphabet;
import automata.Fsa;
import automata.Label;
import automata.State;
import automata.StateSet;
import automata.Transitions;

public abstract class AbstractConstraint implements Serializable {
	
	private static final long serialVersionUID = 1L;
	protected String nameConstraint; 
	protected Fsa contextFsa;
	protected String context;
	protected Fsa sourceFsa;
	protected String leftPath;
	protected String leftValue;
	protected Fsa targetFsa;
	protected String rightPath;
	protected int rightValue;
	protected int npaths = 1;//path defines how the FSA has
	protected Hashtable<Fsa, Hashtable<State, String>> equality = new Hashtable<Fsa, Hashtable<State, String>>();
	protected ArrayList<Integer> j = new ArrayList<Integer>();
	protected String expReg = "^([\\w]((\\s?/\\s?)|(\\s?//\\s?)|(\\s?\\|\\s?)*))*$";
	
	public String getNameConstraint() {
		return nameConstraint;
	}

	public void setNameConstraint(String nameConstraint) {
		this.nameConstraint = nameConstraint;
	}

	public Fsa getContextFsa() {
		return contextFsa;
	}

	public void setContextFsa(Fsa contextFsa) {
		this.contextFsa = contextFsa;
	}

	public String getContext() {
		return context;
	}

	public void setContext(String context) {
		this.context = context;
	}

	public Fsa getSourceFsa() {
		return sourceFsa;
	}

	public void setSourceFsa(Fsa sourceFsa) {
		this.sourceFsa = sourceFsa;
	}

	public String getLeftPath() {
		return leftPath;
	}

	public void setLeftPath(String leftPath) {
		this.leftPath = leftPath;
	}

	public String getLeftValue() {
		return leftValue;
	}

	public void setLeftValue(String leftValue) {
		this.leftValue = leftValue;
	}

	public Fsa getTargetFsa() {
		return targetFsa;
	}

	public void setTargetFsa(Fsa targetFsa) {
		this.targetFsa = targetFsa;
	}

	public String getRightPath() {
		return rightPath;
	}

	public void setRightPath(String rightPath) {
		this.rightPath = rightPath;
	}

	public int getRightValue() {
		return rightValue;
	}

	public void setRightValue(int rightValue) {
		this.rightValue = rightValue;
	}

	public int getNpaths() {
		return npaths;
	}

	public void setNpaths(int npaths) {
		this.npaths = npaths;
	}

	public Hashtable<Fsa, Hashtable<State, String>> getEquality() {
		return equality;
	}

	public void setEquality(Hashtable<Fsa, Hashtable<State, String>> equality) {
		this.equality = equality;
	}

	public ArrayList<Integer> getJ() {
		return j;
	}

	public void setJ(ArrayList<Integer> j) {
		this.j = j;
	}
	
	public void setNPaths(int n){
		npaths = n;
	}
	
	public int getNPaths() {
		return npaths;
	}	

	public void setJ(){
		
		for (State s: sourceFsa.get_final_states()){
			j.add(s.elt);
		}
		
		System.err.println("Vetor J: "+ j.toString());
		
	}
	
	public int getJ(Integer state){
		return j.indexOf(state);
	}
		
	/**
	 * Function that creates a path from the source FSA
	 * And there you have the token |
	 * @param s
	 * @return void, but set new source
	 * @throws Exception 
	 */
	public void setSourceFsa(String s) throws Exception{
		leftPath = new String(s);
		sourceFsa = pathToFsa(s);
		
		for (int i = 0; i < s.length(); i++)  
            if(s.charAt(i) == '|')  
            	npaths++;

		setJ();
	}
	
	/**
	 * Returns if checks node position or value
	 * @param automata
	 * @param node
	 * @return
	 */
	public String getEquality(Fsa automata, State node){
		Hashtable<State, String> h;
		String type = null;
		
		if(equality.containsKey(automata)) {
			h = equality.get(automata);
			type = h.get(node);
		}
		
		return type;
	}
	

	public void setRightValue(String rightPath) {
		if(rightPath.equals("value"))
			this.rightValue = 0;
		else
			this.rightValue = 1;
	}
		
	/**
	 * This function define type {V,N} and FSA (source or target)
	 * @param t
	 * @param side left (l) or right (r)
	 * @return void
	 */
	public void setEquality(String t, String side){
		
		if(side.equals("l"))//get values String and save in variable  
			leftValue = t;
		
		String [] types = t.split(",");
		Fsa f = null;
		
		if(side.equals("l"))
			f = sourceFsa;
		if(side.equals("r"))
			f = targetFsa;
		
		StateSet ss = f.get_final_states();
		int j = 0;
		Iterator<State> i = ss.iterator();
		Hashtable<State, String> h = new Hashtable<State, String>();
		
		while (i.hasNext() || j<types.length){
			h.put(i.next(), types[j]);
			j++;
		}			
		
		equality.put(f, h);
	}
	
	/**
	 * Function that creates FSA (automaton) from a path
	 * @param path
	 * @return new FSA
	 * @throws Exception 
	 */
	public Fsa pathToFsa(String path) throws Exception{
		System.out.println("Path To FSA: " + path);		

		path = path.trim();
		
		if(path.matches(expReg) == false)
			throw new Exception("Error on syntax of the regular expressions.");
		
		Fsa fsa = new Fsa();
		int i = 1;

		Label star = new Label("*");
		
		State start = new State(1);
		State sant;
		State s;
		
		Alphabet alpha = new Alphabet();
		StateSet ss = new StateSet();
		StateSet fs = new StateSet();
		
		Transitions transitions = new Transitions();

		ss.add(start);
		sant = start;
				
	    Pattern pattern = Pattern.compile("[\\w]+\\s*|(/+)|(\\|)*");	    
	    Matcher matcher = pattern.matcher(path);
	    
	    while (matcher.find()) {
	    	
	    	String item = matcher.group().trim();			
	    	
	    	if(!item.equals("")){	    		
				if (item.equals("|")){
					fs.add(sant);
					sant = start;
				} else if (item.equals("//")){
					alpha.add(star);
					StateSet ss2 = new StateSet();
					ss2.add(sant);
					transitions.put(sant, star, ss2);
				} else if ((!item.equals("/")) && (!item.equals("//")) && (!item.equals("|")) ){  									
					Label l = new Label(item);
					
					if(alpha.contains(l)){
						sant = new State (sant.elt+1);
					} else {
						i++;
						alpha.add(l);
						s = new State(i);
						ss.add(s);
						StateSet ss1 = new StateSet();
						ss1.add(s);
						transitions.put(sant, l, ss1);
						sant = s;
					}
				}
	    	}	    		
	    }
	    
		fs.add(sant);
		fsa.set_alphabet(alpha);
		fsa.set_states(ss);
		fsa.set_start_state(start);
		fsa.set_final_states(fs);
		fsa.set_transitions(transitions);
		
		return fsa;	    
	}
	
	/**
	 * Function that creates a path from the context FSA
	 * @param s
	 * @return void, but set new context
	 * @throws Exception 
	 */
	public void setContextFsa(String s) throws Exception {
		context = s;		
		contextFsa = pathToFsa(s);
	}
	
	/**
	 * Function that creates a path from the targetFsta
	 * @param s
	 * @return void, but set new targetFsa
	 * @throws Exception 
	 */
	public void setTargetFsa(String s) throws Exception{
		rightPath = s;
		targetFsa = pathToFsa(s);
	}	

	
	
	public abstract int getFsaPosition(Fsa f);
	public abstract String toString();
	public abstract String toStringPath();	
}
